import math
import pyautogui
import time
import random
import os
import datetime
import json
import json
import os
import sys
import threading
import queue
import pyperclip
import win32gui
import win32process
import psutil
import subprocess
from PyQt5.QtWidgets import (QApplication, QMainWindow, QPushButton, 
                            QTextEdit, QVBoxLayout, QWidget)
from PyQt5.QtCore import Qt, QTimer, QObject, pyqtSignal
from app.interface_walmart.MySQLHelper_walmart import MySQLHelper
import uuid
import random
from datetime import datetime
import traceback
import pytesseract
import requests
from  follow_baidu import baidu_ocr
import base64
from follow_baidu1 import baidu_baidu1
import math
import time
import pygetwindow as gw
import win32con
from baidu_location import baidu_location
from follow_baidu_last import get_baidu3_location_json, get_text_location_inwindow
class WorkSignals(QObject):
    message = pyqtSignal(str)

class WorkThread(threading.Thread):
    def __init__(self, json_path, signal):
        super().__init__()
        self.json_path = json_path
        self.signals = signal
        self._stop_event = threading.Event()

    def run(self):
        """线程执行函数"""
        try:
            
            a=1
            
        except Exception as e:
            self.signals.message.emit(f"错误: {str(e)}\n")

    def stop(self):
        self._stop_event.set()

    def stopped(self):
        return self._stop_event.is_set()

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.work_thread = None
        self.signals = WorkSignals()
        self.signals.message.connect(self.update_memo)
        self.start_btn.click()

    def init_ui(self):
        self.setWindowTitle('RPA跟卖')
        self.setGeometry(100, 100, 800, 600)
        self.start_btn = QPushButton('开始跟卖任务', self)
        self.start_btn.clicked.connect(self.start_task)
        self.text_memo = QTextEdit(self)
        self.text_memo.setReadOnly(True)
        layout = QVBoxLayout()
        layout.addWidget(self.start_btn)
        layout.addWidget(self.text_memo)
        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)
        path1= self.get_latest_process_path()
        print(path1)

        self.dbhelper=MySQLHelper()
        sql='select * from app_zbirdaccount '
        ziniaoaccount=self.dbhelper.query(sql)
        # print(ziniaoaccount)
        for row in ziniaoaccount:
            print(row['loginname'])
            print(row['password'])
            self.ziniao_login(row['id'],row['loginname'],row['password'])
    def get_latest_process_path(self):
        return
        try:
            # 获取前台窗口句柄
            hwnd = win32gui.GetForegroundWindow()
            # 获取进程ID
            _, pid = win32process.GetWindowThreadProcessId(hwnd)
            # 通过进程ID获取进程信息
            process = psutil.Process(pid)
            return process.exe()
        except Exception as e:
            print(f"获取窗口路径失败: {str(e)}")
            return None
    def start_task(self):
        """启动任务线程"""
        if self.work_thread and self.work_thread.is_alive():
            return

        current_dir = os.getcwd()
        json_file_path = os.path.join(current_dir, 'app', 'interface', 'rpa_login.json')
        
        self.work_thread = WorkThread(json_file_path, self.signals)
        self.work_thread.start()
        self.start_btn.setEnabled(False)
    def ziniao_login(self,zbird_account_id,ziniaoaccount,ziniaopassword):
        self.check_country_origin_in_active_window2("Country of Origin")
        # self.check_country_origin_in_active_window()
        self.text_memo.append("开始执行任务...\n")
        self.text_memo.append("正在启动紫鸟客户端...\n")
        pyautogui.FAILSAFE = True
        pyautogui.PAUSE = 1
        orderindex=0
        addtionalheight=(orderindex -1)*88
        # 进行登录操作
        sql=f'select * from app_walmartaccount  where zbird_account_id={zbird_account_id}'
        walmartaccount=self.dbhelper.query(sql)
        if len(walmartaccount)==0:
            return
        
        subprocess.Popen(r"F:\Program Files\ziniao\ziniao.exe")
        path1=self.get_latest_process_path()
        self.set_window_position('ziniao',1439,5)
        
        # while path1 != r"F:\Program Files\ziniao\ziniao.exe":
        #     time.sleep(0.3)
        #     path1=self.get_latest_process_path()

        pyautogui.click(1601,29)
        self.set_window_position('ziniao',1439,5)
        pyautogui.click(1601,29)

        # 第二部,退出当前用户登录
        path1=self.get_latest_process_path()
        print(1,path1)

        pyautogui.click(1518,370)
        path1=self.get_latest_process_path()

        # 第三步
        time.sleep(6.5)

        pyautogui.click(1800, 472)
        pyautogui.press('backspace', presses=20)
        time.sleep(0.5)
        pyperclip.copy(ziniaoaccount)
        pyautogui.hotkey('ctrl', 'v') 
        # 第四步
        pyautogui.click(1550, 542)
        pyautogui.press('backspace', presses=20)
        time.sleep(0.5)
        pyautogui.write( ziniaopassword,interval=0.1)
        pyautogui.press('enter', presses=2)
        dirpath=os.path.join(os.getcwd(),'screenshot')
        os.makedirs(dirpath,exist_ok=True)
        
        # 第五步登录
        time.sleep(5.5)
        path1=self.get_latest_process_path()
        print(5,path1)
        for row2 in walmartaccount:
            sql2=f'select * from app_skus_new  where is_following=1 and walmart_account_id={row2["id"]}'
            data_app_skus=self.dbhelper.query(sql2)
            if len(data_app_skus)>0:
                orderindex=row2['orderindex']
                orderindex=2
                addtionalheight=(orderindex -1)*88
                # 第六步，等待出现新页面，在这里，选择账号，需要按照每个沃尔玛账号的顺序，进行点击
                # time.sleep(1)
                pyautogui.click(1837, 246 + addtionalheight)
                path1=self.get_latest_process_path()
                time.sleep(5.5)
                # self.human_like_move_and_click(172, 279)
                # self.normalize_active_window()
                self.set_window_position('ziniaobrowser.exe',1,1)
                screenshot=pyautogui.screenshot()
                timstamp=time.time()
                fielpath=os.path.join(dirpath,f'{str(timstamp).replace('.','')}.PNG')
                screenshot.save(fielpath, format="PNG")
                json_result=get_baidu3_location_json(fielpath)
                print(6,path1)
                # 第七步，等待出现新页面
                time.sleep(5.5)
                path1=self.get_latest_process_path()
                print(7,path1)
                pyautogui.click(1886, 36)
                pyautogui.click(1847, 20)
                self.human_like_move_and_click(548, 475)
                time.sleep(1.5)
                self.human_like_move_and_click(906,449)
                self.human_like_move_and_click(568,540)
                # # 第八步等待出现新页面
                path1=self.get_latest_process_path()
                print(8,path1)
                time.sleep(6.5)
                # 最后一步登录
                # pyautogui.click(526, 584)
                # 以下为点击catelog,additem
                self.human_like_move_and_click(93, 295) #时间: 75.77s
                self.human_like_move_and_click(1821, 269) #时间: 79.92s
                time.sleep(1.5) 
                path1=self.get_latest_process_path()
                print(9,path1)
                path1=self.get_latest_process_path()
                print(10,path1)
                # 下面的代码，是进行跟卖操作了。
                self.human_like_move_and_click(809, 441)  
                self.human_like_move_and_click(850, 271)  
                self.ziniao_follow(row2 ,data_app_skus)


        # pyautogui.click(76, 293)

        # pyautogui.click(1820, 274)
        
                # 开始循环 跟卖这个吧00766236069722
    def ziniao_follow(self,row_walmart_account,data_app_skus):  
        hoursdiff=12
        username=row_walmart_account['username']
        now = datetime.now()
        code_part =random.choice("ABCDEF")                  # 固定代号
        year_part = str(now.year)[-1]      # 年份最后一位
        month_part = f"{now.month:02d}"    # 两位数月份
        prefix_part =random.choice("GHIJKLM")                # 固定前缀

        for index,row in enumerate(data_app_skus,start=1) :
            try:
                print(row['sku'])
                # pyautogui.scroll(200)
                full_uuid = str(uuid.uuid4())
                first_segment = full_uuid.split('-')[0]
                leng1=random.randint(5,len(first_segment)-1)
                serial_part =first_segment[:leng1] # 4位序
                sku=row['sku']
                price=row['price']
                new_sku_code=f"{code_part}{year_part}{month_part}{prefix_part}{serial_part}"
                print(new_sku_code)
                sql=f'select * from sku_keywords_history where sku="{sku}" and abs(toprice-{price})>0.01'
                data_sku_keywords_history=self.dbhelper.query(sql)
                print(sql)

                if len(data_sku_keywords_history)==0:
                    
                    # 点击upc，然后按下回车
                    self.human_like_move_and_click(257, 383)
                    pyautogui.press('backspace', presses=20)  # 按20次退格键
                    pyautogui.write(row['upc'], interval=0.1)
                    pyautogui.press('enter',presses=2)     
                        # 点击下一步: 
                        # pyautogui.scroll(-200)
                        #                     # 开始进入新页面
                    self.human_like_move_and_click(248, 752)
                    # 以下为5次连续点击

                    self.human_like_move_and_click(508, 409) #时间: 1853.00s
                    pyautogui.write(str(price+2),interval=0.1)
                    pyautogui.press('enter',presses=1)
                    self.country_origin_in_active_window()
                    time.sleep(1.5)

                    self.human_like_move_and_click(288, 635) #时间: 1854.32s
                    pyautogui.write('1',interval=0.1)
                    time.sleep(1.5)
                    pyautogui.click(719, 643) #时间: 1864.14s
                    pyautogui.click(695, 704) #时间: 1865.67s
                    # 上述2行，完成选择单位点击
                    time.sleep(1.5)

                    self.human_like_move_and_click(587, 404)
                    time.sleep(0.1)
    
                    time.sleep(0.5)
                    self.human_like_move_and_click(956, 398)
                    pyautogui.write(row['price']+2, interval=0.1)
                    time.sleep(0.5)
                    pyautogui.write('1',interval=0.1)
                    # 开始选择重量单位
                    self.human_like_move_and_click(1081, 640)
                    self.human_like_move_and_click(1055, 686)
                    # 下面代码生成税号
                    timestamp = str(int(time.time() * 1000))[-6:]
                    # 生成 2 - 4 位随机数
                    random_length = random.randint(1, 3)
                    random_num = str(random.randint(10**(random_length - 1), 10**random_length - 1))
                    # 组合时间戳和随机数
                    numeric_id = timestamp + random_num
                    #可能要搞个判断，如果出现Country of Origin，则不进行下一步
                    if self.check_country_origin_in_active_window2("Country of Origin"):
                        self.human_like_move_and_click(182, 852) #时间: 74.69s
                        
                        for ix in range(32):
                            time.sleep(0.02 *random.random())
                            pyautogui.scroll(-100)  
                        # 滚动后，选择CN# 向下滚动
                        # 选中
                        self.human_like_move_and_click(103, 514) #时间: 85.76s
                        # 点击税码
                        self.human_like_move_and_click(567, 840) #时间: 93.86s
                        # 输入税码
                        pyautogui.write(numeric_id, interval=0.1)
                        
                        # 上面是选择发货地
                        
                    else:    # 上面是点击税号，
                    # 写税号
                        self.human_like_move_and_click(487, 825)
                        pyautogui.write(numeric_id, interval=0.1)

                        # 写税号
                        
                    # 点击提交
                    self.human_like_move_and_click(1852, 1026)
                    # 关闭当前窗口，继续下一次循环
                    time.sleep(5.5)
                    self.human_like_move_and_click(734, 18) 
                    sql=f'insert into sku_keywords_history (sku,opdate,fromprice,toprice,username) values ("{sku}",now(),0,{price},"{username}")'
                    print(sql)
                    self.dbhelper.execute(sql)
                    sql=f'update app_skus_new set newsku="{new_sku_code}",newprice={price} where id={row["id"]}'
                    self.dbhelper.execute(sql)
            except Exception as e:
                print(f'跟卖失败{sku}->{new_sku_code}->{price}')
                traceback.print_exc()    
    def human_like_move_and_click(self,dest_x, dest_y, duration=0.8, click=True):
        """模拟人类鼠标移动
        :param dest_x: 目标X坐标
        :param dest_y: 目标Y坐标
        :param duration: 移动总时间（秒）
        :param click: 是否最后执行点击
        """
        try:
            # 获取初始位置
            start_x, start_y = pyautogui.position()
            
            # 计算移动路径参数
            distance = math.hypot(dest_x - start_x, dest_y - start_y)
            steps = max(int(distance / 150), 8)  # 至少10步
            step_time = duration / steps
            
            # 生成带扰动的移动路径
            for i in range(steps):
                # 使用缓动函数（easeOutQuad）
                t = (i + 1) / steps
                progress = 1 - (1 - t) ** 2
                current_x = start_x + (dest_x - start_x) * progress
                current_y = start_y + (dest_y - start_y) * progress
                
                # 添加随机扰动（不超过5像素）
                current_x += random.uniform(-3, 3)
                current_y += random.uniform(-3, 3)
                
                # 确保坐标在屏幕范围内
                current_x = max(0, min(pyautogui.size().width-100, current_x))
                current_y = max(0, min(pyautogui.size().height-100, current_y))
                
                pyautogui.moveTo(current_x, current_y, duration=step_time)
                
                # 添加随机延迟（最后几步更慢）
                if i > steps - 3:
                    time.sleep(random.uniform(0.02, 0.05))
            
            # 最终精确移动
            pyautogui.moveTo(dest_x, dest_y, duration=0.1)
            
            if click:
                # 模拟人类点击（按下和释放分开）
                pyautogui.mouseDown()
                time.sleep(random.uniform(0.05, 0.1))
                pyautogui.mouseUp()
                pyautogui.click(dest_x,dest_y,interval=0.1,duration=random.uniform(1.2, 2.05))
                time.sleep(random.uniform(0.5, 1.1))

        except Exception as e:
            print(f"鼠标移动失败: {str(e)}")        
    def normalize_active_window(self):

        try:
            def callback(hwnd, _):
                try:
                    if not win32gui.IsWindowVisible(hwnd):
                        return
                    
                    # 精确获取进程路径
                    _, pid = win32process.GetWindowThreadProcessId(hwnd)
                    process = psutil.Process(pid)
                    exe_path = process.exe() or ""
                    
                    if "ziniaobrowser.exe" not in exe_path.lower():
                        return

                    # 获取窗口当前状态
                    placement = win32gui.GetWindowPlacement(hwnd)
                    
                    # 强制解除所有可能的最大化状态
                    if placement[1] != win32con.SW_SHOWNORMAL:
                        win32gui.ShowWindow(hwnd, win32con.SW_HIDE)  # 先隐藏窗口
                        time.sleep(0.1)
                        win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)
                        time.sleep(0.2)
                        win32gui.ShowWindow(hwnd, win32con.SW_SHOWNOACTIVATE)
                    
                    # 彻底移除所有可能的最大化相关样式
                    style = win32gui.GetWindowLong(hwnd, win32con.GWL_STYLE)
                    new_style = style & ~(win32con.WS_MAXIMIZEBOX | 
                                        win32con.WS_MAXIMIZE | 
                                        win32con.WS_THICKFRAME)
                    win32gui.SetWindowLong(hwnd, win32con.GWL_STYLE, new_style)
                    
                    # 获取实际窗口尺寸（考虑DPI缩放）
                    rect = win32gui.GetWindowRect(hwnd)
                    dpi = win32gui.GetDpiForWindow(hwnd)
                    scale_factor = dpi / 96.0
                    width = int((rect[2] - rect[0]) / scale_factor)
                    height = int((rect[3] - rect[1]) / scale_factor)
                    
                    # 原子操作：设置窗口位置和尺寸
                    win32gui.SetWindowPos(
                        hwnd,
                        win32con.HWND_BOTTOM,  # 防止层级变化触发最大化
                        1, 1,
                        width,
                        height,
                        win32con.SWP_FRAMECHANGED |
                        win32con.SWP_NOZORDER |
                        win32con.SWP_NOOWNERZORDER |
                        win32con.SWP_NOSENDCHANGING
                    )
                    
                    # 强制更新窗口状态
                    win32gui.UpdateWindow(hwnd)
                    win32gui.SetWindowPos(
                        hwnd,
                        win32con.HWND_TOP,
                        0, 0, 0, 0,
                        win32con.SWP_NOMOVE |
                        win32con.SWP_NOSIZE |
                        win32con.SWP_NOOWNERZORDER
                    )
                    
                    # 最终验证
                    for _ in range(3):
                        current_placement = win32gui.GetWindowPlacement(hwnd)
                        if current_placement[1] == win32con.SW_SHOWNORMAL:
                            break
                        time.sleep(0.1)
                        win32gui.ShowWindow(hwnd, win32con.SW_RESTORE)

                except Exception as e:
                    print(f"窗口处理异常: {str(e)}")

            # 执行四次确保状态稳定
            for _ in range(4):
                win32gui.EnumWindows(callback, None)
                time.sleep(0.3)
                # 额外处理父窗口
                win32gui.EnumWindows(lambda hwnd, _: callback(hwnd, None), None)
            
            return True
        except Exception as e:
            print(f"窗口恢复失败: {str(e)}")
            return False
    def get_latest_process_path(self):
        """获取最新打开的窗口程序路径"""
        try:
            latest_time = 0
            latest_path = None
            # 遍历所有进程找最新创建的
            for proc in psutil.process_iter(['pid', 'name', 'create_time', 'exe']):
                try:
                    if proc.info['create_time'] > latest_time:
                        latest_time = proc.info['create_time']
                        latest_path = proc.info['exe']
                except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                    continue
                    
            return latest_path
        except Exception as e:
            print(f"获取最新进程路径失败: {str(e)}")
            return None
    def get_latest_process_path(self):
        """获取当前最前窗口的程序路径"""
        try:
            # 获取前台窗口句柄
            hwnd = win32gui.GetForegroundWindow()
            # 获取进程ID
            _, pid = win32process.GetWindowThreadProcessId(hwnd)
            # 通过进程ID获取进程信息
            process = psutil.Process(pid)
            return process.exe()
        except Exception as e:
            print(f"获取窗口路径失败: {str(e)}")
            return None
     
    def list_all_windows(self):
        """列出所有可见窗口及其可执行文件路径"""
        windows_list = []

        def enum_callback(hwnd, _):
            try:
                # 过滤不可见窗口和空标题窗口
                if not win32gui.IsWindowVisible(hwnd):
                    return
                
                window_title = win32gui.GetWindowText(hwnd)
                if not window_title:
                    return

                # 获取进程信息
                _, pid = win32process.GetWindowThreadProcessId(hwnd)
                exe_path = "N/A"
                
                try:
                    process = psutil.Process(pid)
                    exe_path = process.exe() or "N/A"
                except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
                    exe_path = "Access Denied"
                except Exception as e:
                    exe_path = f"Error: {str(e)}"

                # 添加到列表
                windows_list.append({
                    'hwnd': hwnd,
                    'title': window_title,
                    'exe': exe_path,
                    'pid': pid
                })

            except Exception as e:
                print(f"处理窗口 {hwnd} 时出错: {str(e)}")

        # 枚举所有窗口
        win32gui.EnumWindows(enum_callback, None)
        
        # 打印结果
        print("\n当前所有窗口列表：")
        print("{:<10} {:<60} {:<80}".format('PID', 'Window Title', 'EXE Path'))
        for window in windows_list:
            print("{:<10} {:<60} {:<80}".format(
                window['pid'],
                window['title'][:55] + '...' if len(window['title']) > 55 else window['title'],
                window['exe'][:75] + '...' if len(window['exe']) > 75 else window['exe']
            ))
        
        return windows_list
    def set_window_position(self,exename,destx,desty):
        try:
            def callback(hwnd, _):
                try:
                    # 获取窗口的进程ID
                    _, pid = win32process.GetWindowThreadProcessId(hwnd)
                    # 获取进程对象
                    process = psutil.Process(pid)
                    exe_path = process.exe() or ""
                    # 检查是否包含ziniao（不区分大小写）
                    if exename in exe_path.lower():
                        # 获取当前窗口尺寸
                        left, top, right, bottom = win32gui.GetWindowRect(hwnd)
                        width = right - left
                        height = bottom - top
                        # 使用Windows API移动窗口
                        win32gui.MoveWindow(hwnd, destx,desty, width, height, True)
                except (psutil.NoSuchProcess, psutil.AccessDenied):
                    return

            # 枚举所有窗口
            win32gui.EnumWindows(callback, None)
            return True
        except Exception as e:
            print(f"窗口移动失败: {e}")
            return False
    def update_memo(self, message):
        """线程安全更新日志"""
        self.text_memo.append(message)

    def closeEvent(self, event):
        """窗口关闭时停止线程"""
        if self.work_thread and self.work_thread.is_alive():
            self.work_thread.stop()
            self.work_thread.join()
        event.accept()

def main():
    # 设置安全选项
    a=2
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

